//
//  LWRefreshControl.swift
//  LW_Weibo
//
//  Created by 刘伟 on 16/7/16.
//  Copyright © 2016年 LWSpro. All rights reserved.
//

import UIKit

private let LWRefreshControlHeight: CGFloat = 50



//下拉是的状态  定义成枚举
enum LWRefreshControlState: Int {
    
    
    //下拉刷新状态
    case Normal = 0
    
    //松手刷新状态
    case Pulling = 1
    
    //正在刷新
    case Refreshing = 2
    
    
    
}



class LWRefreshControl: UIControl {

    //当前下拉刷新时的状态  一开始默认为下拉刷新
    var refreshState: LWRefreshControlState = .Normal {
        
        didSet {  //改变状态的时候,就会走这个方法 可以在这里面完成事件
            
            switch refreshState {
                
            case .Normal:  //下拉刷新  需要做哪些事情
                
                //箭头向下  告诉用户下拉刷新
                messageLabel.text = "下拉刷新"
                
                //显示箭头  因为下面要隐藏箭头显示菊花转,所以要把箭头显示
                iconImageView.hidden = false

                //保证箭头一直向下  以动画的形式
                UIView.animateWithDuration(0.36, animations: { () -> Void in
                    
                    self.iconImageView.transform = CGAffineTransformIdentity
                    
                })
               
                //当下拉转态是  就关闭菊花转  关闭菊花转 视图自动隐藏
                indicatorView.stopAnimating()
                
                //恢复这个状态后 我们让内容顶部距离恢复  
                //这时需要判断一下  如果上一个状态是'正在刷新' 即Refreshing 时  那么才恢复 不然要是从Pulling状态回到 Normal状态 那么内容就会减少一个高度 不断重复 那么就会一直减少
                
                //判断上一次的状态
                if oldValue == .Refreshing {
                    
                    UIView.animateWithDuration(0.36, animations: { () -> Void in
                        
                        self.currentScrollView?.contentInset.top -= LWRefreshControlHeight
                        
                    })
                    
                    
                }
   
                
            case .Pulling: //松手就刷新  需要做哪些事情
                
                messageLabel.text = "松手,刷新"
                
                //让箭头向上
                UIView.animateWithDuration(0.36, animations: { () -> Void in
                    
                    
                    self.iconImageView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
                    
                    
                })
                
                
            case .Refreshing: //正在刷新  需要做哪些事情
              
                //隐藏箭头
                iconImageView.hidden = true
                
                //如果是正在刷新 那么就开启菊花转
                indicatorView.startAnimating()
                
                messageLabel.text = "正在刷新..."
                
                //让正在刷新停留一段时间 即让messageLabel 停留一段时间 让用户知道正在刷新
                //让内容的顶部距离屏幕顶部多一个高度
                UIView.animateWithDuration(0.36, animations: { () -> Void in
                    
                     self.currentScrollView?.contentInset.top += LWRefreshControlHeight
                    
                    
                })
                
                
                //发送消息   告诉响应者此时状态下的需要刷新事件
                sendActionsForControlEvents(.ValueChanged)

                
            }
 
            
            
        }
        
        
        
    }
    
    
    
    //MARK: - 模拟系统方法
    //系统有一个endRefreshing()的方法  调用就会关闭菊花转  那么我们就来 模仿  
    func endRefreshing() {  //要被外界调用 那么就不能设置成私有方法
        
        //只需改变状态  那么就会走didSet 方法  在didSet 方法里面 我们关闭菊花转就行
        //'Refreshing' 的状态过后就是'Normal'了  所以我们只需要恢复成'Normal'状态就行  那些关闭菊花转的逻辑只需写在didSet 方法中就行  因为状态改变 就会走 didSet
        
        refreshState = .Normal
        
        
        
    }
    
    
    
    //MARK: - 懒加载控件
    //下拉的箭头
    private lazy var iconImageView: UIImageView = UIImageView(image: UIImage(named: "tableview_pull_refresh"))
    
    
    //下拉显示的内容
    private lazy var messageLabel: UILabel = {
        
       let label = UILabel() //用系统的创建 不要用抽取的分类 这样就可以在任何地方使用了
        
        label.font = UIFont.systemFontOfSize(12)
        
        label.textColor = UIColor.grayColor()
        
        label.text = "下拉刷新"  //先默认来个内容
        
        return label
        
    }()
    
    
    
    //菊花转
    private lazy var indicatorView: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
    
    
    
    
    
    
    
    //记录父视图
    var currentScrollView: UIScrollView?
    
    
    override init(frame: CGRect) {
        
        super.init(frame: frame)
        
        layoutUI()
        
    }

    
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    
    //MARK: - 布局
    private func layoutUI() {
        
        //给个背景
        backgroundColor = RandomColor()
        
        //添加
        addSubview(iconImageView)
        addSubview(messageLabel)
        addSubview(indicatorView)
        
        
        //添加约束 用系统的  要封装 那么就都使用系统的 这样不需要依靠第三方的  在任何地方都可以使用到
        
        
        
        /*
        
        第一个参数 是我们要约束的那个控件
        第二个参数 约束的位置
        第三个参数 一般就填 Equal
        第四个参数 要约束的那个控件是相对于哪一个控件的
        第五个参数 相对的那个控件的位置
        第六个参数 一般写1
        第七个参数 约束多少

        */
        
        //使用手动布局 必须要写上这个  false表示允许手动布局
        iconImageView.translatesAutoresizingMaskIntoConstraints = false
        //箭头的约束
        addConstraint(NSLayoutConstraint(item: iconImageView, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: -35))
        
        addConstraint(NSLayoutConstraint(item: iconImageView, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1, constant: 0))
        
        //内容约束
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .Leading, relatedBy: .Equal, toItem: iconImageView, attribute: .Trailing, multiplier: 1, constant: 0))
        addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .CenterY, relatedBy: .Equal, toItem: iconImageView, attribute: .CenterY, multiplier: 1, constant: 0))
        
        
        //菊花转约束
        indicatorView.translatesAutoresizingMaskIntoConstraints = false
        addConstraint(NSLayoutConstraint(item: indicatorView, attribute: .CenterX, relatedBy: .Equal, toItem: iconImageView, attribute: .CenterX, multiplier: 1, constant: 0))
        addConstraint(NSLayoutConstraint(item: indicatorView, attribute: .CenterY, relatedBy: .Equal, toItem: iconImageView, attribute: .CenterY, multiplier: 1, constant: 0))
        

        
        
        
    }
    
    
    //MARK: - 系统有个拿到父控件的方法
    override func willMoveToSuperview(newSuperview: UIView?) {
        
        //这里可以获取到父控件 newSuperview
        
        //首先判断其父控件能否滚动  不能滚动的话自然就不能下拉了 滚动视图都继承自UIScrollView
        guard let superView = newSuperview as? UIScrollView else {
            
            
            //在这里面就不是UIScrollView的子类
            return
            
            
        }
        
        
        //到这里父控件就是UIScrollView的子类  既然能在这里拿到父控件 那么我们对其的frame赋值 就可以写到这里了
        
        
        //设置一个size  以及位置  为了保证在任何地方都能用到 用系统的frame来设置  记住  frame只有在子控件加入到父控件的时候才起作用 因为frame就是相对于父控件的
        self.frame.origin.y = -LWRefreshControlHeight
        
        //和xib 一样 让它一出来就有一个大小(Size)
        self.frame.size = CGSize(width: superView.frame.size.width, height: LWRefreshControlHeight)
        
        //使用kvo  来监听父控件的滚动
        superView.addObserver(self, forKeyPath: "contentOffset", options: [.New], context: nil)
       
        //用来kvo  一定要记得释放  和通知一样
        currentScrollView = superView
        
    }
    
    
    
    
    
    //MARK: - kvo监听父控件滚动
    //kvo来监控父控件的滚动  不要去用代理 因为代理是一对一的 数据源和代理已经设置了 如果再去设置代理 那么数据源和代理就会失效
    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        
        //这里面  只要父控件滚动 就会调用这个系统方法  所以我们能在这里拿到父控件的偏移量
        //父控件已经记录下来了 
        //这里我们为防止意外  先判断能不能解包
        guard let scrollView = currentScrollView else {
            
            return
            
        }
        
        
        
        //这里已经解包了  scrollView.contentInset.top 视图内容距离屏幕的顶部距离
         if scrollView.dragging { //拖拽时  最好把这个不怎么变动的判断条件写在最外层 变动太大的写在最外层 很可能出问题 当再次遇到这种外层和内层都有判断条件的问题时,可以考虑互换一下内外层的判断条件  看哪种简单
        
            if scrollView.contentOffset.y < -(scrollView.contentInset.top + LWRefreshControlHeight) && refreshState == .Normal { //加上后面的判断是为了让处于下拉刷新状态时,不要在打印'松手就刷新'了
            
           
                
                print("松手就刷新")
                //状态是一个属性 我们改变状态的同时需要做某些事情 而改变状态就会走set方法  所以要干什么事就在didSet里面写
                 refreshState = .Pulling  //改变状态
                
               }else if scrollView.contentOffset.y >= -(scrollView.contentInset.top + LWRefreshControlHeight) && refreshState == .Pulling {
                
               print("下拉刷新")
                
                
                refreshState = .Normal
                
                
            }
            
            
        }else { //松手后
            
            if refreshState == .Pulling { //如果是松手刷新的状态才在松手后 显示正在刷新
                
                print("正在刷新")
                
                refreshState = .Refreshing
                
            }
            
            
            
            
        }
        
 
        
        
    }
    
    
    
    
    
    
    
    
    
    
    
    //MARK: - 移除kvo
    deinit {
        
        
        currentScrollView?.removeObserver(self, forKeyPath: "contentOffset")
        
        print("已经移除了")
        
    }
    
    
    
    

}
